home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / smailsrc.zip / UUPC.ZIP / DCPGPKT.C < prev    next >
Text File  |  1990-04-21  |  19KB  |  757 lines

  1. /*
  2.     For best results in visual layout while viewing this file, set
  3.     tab stops to every 8 columns.
  4. */
  5.  
  6. /*
  7.     dcpgpkt.c
  8.  
  9.     Revised edition of dcp
  10.  
  11.     Stuart Lynne May/87
  12.  
  13.     Copyright (c) Richard H. Lamb 1985, 1986, 1987
  14.     Changes Copyright (c) Stuart Lynne 1987
  15.  
  16.     Maintenance notes:
  17.  
  18.     25Aug87 - Allow for up to 7 windows - Jal
  19.     01Nov87 - those strncpy's should really be memcpy's! - Jal
  20. */
  21.  
  22. /* "DCP" a uucp clone. Copyright Richard H. Lamb 1985,1986,1987 */
  23.  
  24. /*
  25.  *  Cleaned up by Stephen Trier, April 21, 1990
  26.  */
  27.  
  28. /* 7-window "g" ptotocol */
  29.  
  30. /*
  31.    Thanks goes to John Gilmore for sending me a copy of Greg Chesson's
  32.    UUCP protocol description -- Obviously invaluable.
  33.    Thanks also go to Andrew Tannenbaum for the section on Siding window
  34.    protocols with a program example in his "Computer Networks" book.
  35. */
  36.  
  37. #include "dcp.h"
  38.  
  39. #define PKTSIZE 64
  40. #define PKTSIZ2 2   /* 8x(2**2) = 64 */
  41.  
  42. #define HDRSIZE 6
  43. #define MAXTRY  4
  44.  
  45. /* g-packet type definitions */
  46.  
  47. #define DATA    0
  48. #define CLOSE   1
  49. #define NAK     2
  50. #define SRJ     3
  51. #define ACK     4
  52. #define INITC   5
  53. #define INITB   6
  54. #define INITA   7
  55.  
  56. #define MAXERR  200 /* Don't want to quit in a middle of a long file */
  57. #define TIMEOUT 4   /* could be longer */
  58. #define KPKT    1024/PKTSIZE
  59. #define POK     -1
  60.  
  61. #define MAXWINDOW   7
  62. #define SWINDOW 3   /* fixed now, you make it variable! (done.) */
  63. #define RWINDOW 3
  64. #define NBUF    8   /* always SAME as MAXSEQ ? */
  65. #define MAXSEQ  8
  66.  
  67. #define between(a,b,c) ((a<=b && b<c) || (c<a && a<=b) || (b<c && c<a))
  68.  
  69. /* packet definitions */
  70.  
  71. static int rwl, swl, swu, rwu, nerr, nbuffers, npkt, irec, timeout;
  72. static int GOT_SYNC, GOT_HDR;
  73. static int fseq[NBUF], outlen[NBUF], inlen[NBUF], arrived[NBUF];
  74. static int nwindows;
  75. static char outbuf[NBUF][PKTSIZE+1], inbuf[NBUF][PKTSIZE+1];
  76. static unsigned char grpkt[HDRSIZE+1];
  77. static long ftimer[NBUF], acktmr, naktmr;
  78.  
  79. static void gspack();
  80. static int grpack(), gmachine();
  81.  
  82.  
  83. /****************** SUB SUB SUB PACKET HANDLER ************/
  84.  
  85. /*
  86.     g o p e n p k
  87. */
  88.  
  89. int gopenpk()
  90. {
  91.     int i, xxx, yyy, len;
  92.     char tmp[PKTSIZE+1];
  93.  
  94.     pktsize = PKTSIZE;  /* change it later after the init */
  95.     msgtime = MSGTIME;  /* not sure I need this for "g" proto */
  96.  
  97.     /* initialize proto parameters */
  98.     swl = nerr = nbuffers = npkt = 0;
  99.     swl = swu = 1;
  100.     rwl = 0;
  101.     rwu = MAXWINDOW - 1;
  102.     nwindows = MAXWINDOW;
  103.     for (i = 0; i < NBUF; i++) {
  104.         ftimer[i] = 0;
  105.         arrived[i] = FALSE;
  106.     }
  107.     GOT_SYNC = GOT_HDR = FALSE;
  108.  
  109.     /* 3-way handshake */
  110.     timeout = 2; /* want some timeout capability here */
  111.     gspack(INITA, 0, 0, 0, tmp);
  112. rsrt:
  113.     if (nerr >= MAXERR)
  114.         return(FAILED);
  115.  
  116.     /* INIT sequence. Easy fix for variable packet size.
  117.        I didn't since all the machines I talk to use PKTSZ=64.
  118.        If you do this make sure to reflect the changes in "grpack"
  119.        and "gspack" */
  120.  
  121.     switch (grpack(&yyy, &xxx, &len, tmp)) {
  122.     case INITA:
  123.         gspack(INITB, 0, 0, 0, tmp);    /* data segment (packet) size */
  124.         nwindows = yyy;
  125.         if (nwindows > MAXWINDOW)
  126.             nwindows = MAXWINDOW;
  127.         rwu = nwindows - 1;
  128.         goto rsrt;
  129.     case INITB:
  130.         gspack(INITC, 0, 0, 0, tmp);
  131.         goto rsrt;
  132.     case INITC:
  133.         break;
  134.     default:
  135.         nerr++;
  136.         gspack(INITA, 0, 0, 0, tmp);
  137.         goto rsrt;
  138.     }
  139.  
  140.     nerr = 0;
  141.     return(OK); /* channel open */
  142.  
  143. } /*gopenpk*/
  144.  
  145.  
  146. /*
  147.     g c l o s e p k
  148. */
  149.  
  150. int gclosepk()
  151. {
  152.     int i;
  153.     char tmp[PKTSIZE+1];
  154.  
  155.     timeout = 1;
  156.     for (i = 0; i < MAXTRY; i++) {
  157.         gspack(CLOSE, 0, 0, 0, tmp);
  158.         if (gmachine() == CLOSE)
  159.             break;
  160.     }
  161.  
  162.     printmsg(0, "%d packets transferred, %d errors.", npkt, nerr);
  163.  
  164.     return(0);
  165.  
  166. } /*gclosepk*/
  167.  
  168.  
  169. /*
  170.     g g e t p k t
  171.  
  172.     Gets no more than a packet's worth of data from
  173.     the "packet I/O state machine".  May have to
  174.     periodically run the packet machine to get some packets.
  175.  
  176.     on input:   don't care
  177.     on return:  data+\0 and length in len.
  178.  
  179.     ret(0)  if all's well
  180.     ret(-1) if problems (failed)
  181. */
  182.  
  183. int ggetpkt(data, len)
  184. char *data;
  185. int *len;
  186. {
  187.     int i;
  188.  
  189.     irec = 1;
  190.     timeout = 0;
  191.  
  192.     /* LOOP TO WAIT FOR THE DESIRED PACKET */
  193.     while ((arrived[rwl]) == FALSE)
  194.         if (gmachine() != POK)
  195.             return(-1);
  196.  
  197.     /* GOT A PACKET! */
  198.     i = rwl;    /*<-- mod(,rwindow) for larger than 8 seq no.s */
  199.     *len = inlen[i];
  200.     memcpy(data, inbuf[i], *len);
  201.  
  202.     arrived[i] = FALSE;
  203.     rwu = (rwu + 1) % MAXSEQ;   /* bump receive window */
  204.     npkt++;
  205.  
  206.     return(0);
  207.  
  208. } /*ggetpkt*/
  209.  
  210.  
  211. /*
  212.     g s e n d p k t
  213.  
  214.     Put at most a packet's worth of data in the packet state
  215.     machine for transmission.
  216.     May have to run the packet machine a few times to get
  217.     an available output slot.
  218.  
  219.     on input: data=*data; len=length of data in data.
  220.     flg = 2 just send the packet with no wait for ack.
  221.     flg > 0 zero out the unused part of the buffer. (for UUCP "msg" pkts)
  222.     flg = 0 normal data
  223.  
  224.     return:
  225.      0  if all's well
  226.     -1  if problems (failed)
  227. */
  228.  
  229. int gsendpkt(data, len, flg)
  230. char *data;
  231. int len, flg;
  232. {
  233.     int i1;
  234.  
  235.     irec = 0;
  236.     timeout = 0; /* non-blocking reads */
  237.     /* WAIT FOR INPUT i.e. if weve sent SWINDOW pkts and none have been
  238.        acked, wait for acks */
  239.     while (nbuffers >= nwindows)
  240.         if (gmachine() != POK)
  241.             return(-1);
  242.  
  243.     i1 = swu;   /* <--If we ever have more than 8 seq no.s, must mod() here */
  244.  
  245.     /* PLACE PACKET IN TABLE AND MARK UNACKED */
  246.  
  247.     /* fill with zeros or not */
  248.     if (flg) {
  249.         int i;
  250.         strcpy(outbuf[i1], data);
  251.         for (i = strlen(data); i < PKTSIZE; i++)
  252.             outbuf[i1][i] = '\0';
  253.         len = PKTSIZE;
  254.     } else {
  255.         memcpy(outbuf[i1], data, len);
  256.         outbuf[i1][len] = '\0';
  257.     }
  258.  
  259.     /* mark packet */
  260.     outlen[i1] = len;
  261.     ftimer[i1] = time(nil(long));
  262.     fseq[i1] = swu;
  263.     swu = (swu + 1) % MAXSEQ;   /* bump send window */
  264.     nbuffers++;
  265.     npkt++;
  266.  
  267.     /* send it */
  268.     gspack(DATA, rwl, fseq[i1], outlen[i1], outbuf[i1]);
  269.     /* send it once then let the packet machine take it.
  270.        Wouldn't need this for multi-tasking systems. */
  271.     /* sl gmachine(); */
  272.  
  273.     return(0);
  274.  
  275. } /*gsendpkt*/
  276.  
  277.  
  278. /**********  Packet Machine  ********** RH Lamb 3/87 */
  279.  
  280. /*
  281.     g m a c h i n e
  282.  
  283.     Ideally we would like to fork this process off in an infinite loop and
  284.     send and receive packets through "inbuf" and "outbuf". Can't do this in
  285.     MS-DOS so we setup "getpkt" and "sendpkt" to call this routine often and
  286.     return only when the input buffer is empty thus "blocking" the packet-
  287.     machine task.
  288. */
  289.  
  290. static int gmachine()
  291. {
  292.     int rack, rseq, rlen, i1, i2, dflg;
  293.     char rdata[PKTSIZE+1];
  294.     long itmp;
  295.  
  296. reply:
  297.     printmsg(10, "* send %d < W < %d, receive %d < W < %d, error %d",
  298.         swl, swu, rwl, rwu, nerr);
  299.     /* waiting for ACKs for swl to swu-1. Next pkt to send=swu */
  300.     /* rwl=expected pkt */
  301.     printmsg(7, "Kbytes transfered %d errors %d", npkt / KPKT, nerr);
  302.     if (nerr >= MAXERR)
  303.         goto close;
  304.     dflg = 0;
  305.  
  306.     switch (grpack(&rack, &rseq, &rlen, rdata)) {
  307.  
  308.     case CLOSE:
  309.         printmsg(5, "**got CLOSE");
  310.         goto close;
  311.  
  312.     case NAK:
  313.         nerr++;
  314.         acktmr = naktmr = 0;    /* stop ack/nak timer */
  315.         printmsg(5, "**got NAK %d", rack);
  316. nloop:
  317.         if (between(swl, rack, swu)) {  /* resend rack->(swu-1) */
  318.             i1 = rack;
  319.             gspack(DATA, rwl, rack, outlen[i1], outbuf[i1]);
  320.             printmsg(5, "*** resent %d", rack);
  321.             ftimer[i1] = time(nil(long));
  322.             rack = (rack + 1) % MAXSEQ;
  323.             goto nloop;
  324.         }
  325.         if (dflg)
  326.             return(POK);
  327.         goto reply; /* any other stuff ? */
  328.  
  329.     case EMPTY:
  330.         printmsg(5, "**got EMPTY");
  331.         itmp = time(nil(long));
  332.         if (acktmr)
  333.             if ((itmp - acktmr) >= TIMEOUT) {   /* ack timed out*/
  334.                 gspack(ACK, rwl, 0, 0, rdata);
  335.                 acktmr = itmp;
  336.             }
  337.         if (naktmr)
  338.             if ((itmp - naktmr) >= TIMEOUT) {   /*nak timed out*/
  339.                 gspack(NAK, rwl, 0, 0, rdata);
  340.                 naktmr = itmp;
  341.             }
  342.         /* resend any timed out un-acked pkts */
  343.         for (i2 = swl; between(swl, i2, swu); i2 = (1 + i2) % MAXSEQ) {
  344.             acktmr = naktmr = 0; /* reset ack/nak */
  345.             i1 = i2;
  346.             printmsg(5, "---> seq, elapst %d %ld", i2, (itmp - ftimer[i1]));
  347.             if ((itmp - ftimer[i1]) >= TIMEOUT) {
  348.                 printmsg(5, "*** timeout %d", i2);
  349.                 /* Since "g" is "go-back-N", when we time out we
  350.                    must send the last N pkts in order.  The generalized
  351.                    sliding window scheme relaxes this reqirment. */
  352.                 nerr++;
  353.                 dflg = 1;   /* same hack */
  354.                 rack = i2;
  355.                 goto nloop;
  356.             }
  357.         }
  358.         return(POK);
  359.  
  360.     case ACK:
  361.         printmsg(5, "**got ACK %d", rack);
  362.         acktmr = naktmr = 0;    /* disable ack/nak's */
  363. aloop:
  364.         if (between(swl, rack, swu)) {  /* S<-- -->(S+W-1)%8 */
  365.             printmsg(5, "*** ACK %d", swl);
  366.             ftimer[swl] = 0;
  367.             nbuffers--;
  368.             swl = (1 + swl) % MAXSEQ;
  369.             dflg = 1;   /* same hack */ /* sl */
  370.             goto aloop;
  371.         }
  372.         if (dflg)
  373.             return(POK);    /* hack for non-mtask sys's */
  374.         /* to empty "inbuf[]" */
  375.         goto reply;
  376.  
  377.     case DATA:
  378.         printmsg(5, "**got DATA %d %d", rack, rseq);
  379.         i1 = (rwl + 1) % MAXSEQ;    /* (R+1)%8 <-- -->(R+W)%8 */
  380.         i2 = (rwu + 1) % MAXSEQ;
  381.         if (between(i1, rseq, i2)) {
  382.             if (i1 == rseq) {
  383.                 i1 = rseq;
  384.                 arrived[i1] = TRUE;
  385.                 inlen[i1] = rlen;
  386.                 memcpy(inbuf[i1], rdata, rlen);
  387.                 rwl = (rwl + 1) % MAXSEQ;
  388.                 printmsg(5, "*** ACK d %d", rwl);
  389.                 gspack(ACK, rwl, 0, 0, rdata);
  390.                 acktmr = time(nil(long));   /* enable ack/nak tmout*/
  391.                 dflg = 1;   /* return to call when finished */
  392.                 /* in a mtask system, unneccesary */
  393.             } else {
  394.                 nerr++;
  395.                 printmsg(5, "*** unexpect %d ne %d", rseq, rwl);
  396.             }
  397.         } else {
  398.             nerr++;
  399.             printmsg(5, "*** wrong seq %d", rseq);
  400.         }
  401.         goto aloop;
  402.  
  403.     case ERROR:
  404.         nerr++;
  405.         printmsg(5, "*** got BAD CHK");
  406.         gspack(NAK, rwl, 0, 0, rdata);
  407.         naktmr = time(nil(long));   /* set nak timer */
  408.         printmsg(5, "*** NAK d %d", rwl);
  409.         goto reply;
  410.  
  411.     default:
  412.         printmsg(5, "*** got SCREW UP");
  413.         goto reply; /* ignore it */
  414.  
  415.     }
  416.  
  417. close:
  418.     gspack(CLOSE, 0, 0, 0, rdata);
  419.     return(CLOSE);
  420.  
  421. } /*gmachine*/
  422.  
  423.  
  424. /*************** FRAMMING *****************************/
  425.  
  426. /*
  427.     g s p a c k
  428.  
  429.     Send a packet
  430.  
  431.     type=type yyy=pkrec xxx=timesent len=length<=PKTSIZE data=*data
  432.     ret(0) always
  433. */
  434.  
  435. static void gspack(type, yyy, xxx, len, data)
  436. int type, yyy, xxx, len;
  437. char data[];
  438. {
  439.     unsigned int check, i;
  440.     unsigned char header[HDRSIZE+1];
  441.  
  442.     /***** Link Testing Mods *****/
  443. /*  unsigned char   dpkerr[10]; /**/
  444.     /***** End Link Testing Mods *****/
  445.  
  446.     if (len > PKTSIZE)  /* just in case */
  447.         len = PKTSIZE;
  448.     if (len == 0)
  449.         data[0] = '\0';
  450.  
  451.     /***** Link Testing Mods - create artificial errors *****/
  452. /*  printf("**n:normal,e:error,l:lost,p:partial,h:bad header,s:new seq--> ");
  453.     gets(dpkerr);
  454.     if (dpkerr[0] == 's')
  455.         sscanf(&dpkerr[1], "%d", &xxx); /**/
  456.     /***** End Link Testing Mods *****/
  457.  
  458.     printmsg(5, "send packet type %d, yyy=%d, xxx=%d, len=%d",
  459.         type, yyy, xxx, len);
  460.     printmsg(5, "data=|%s|", data);
  461.     header[0] = '\020';
  462.     type %= 8;
  463.     header[4] = type << 3;
  464.     switch (type) {
  465.     case CLOSE:
  466.         break;  /* stop protocol */
  467.     case NAK:
  468.         header[4] += yyy;
  469.         break;  /* reject */
  470.     case SRJ:
  471.         break;
  472.     case ACK:
  473.         header[4] += yyy;
  474.         break;  /* ack */
  475.     case INITC:
  476.         header[4] += nwindows;
  477.         break;
  478.     case INITB:
  479.         header[4] += 1;
  480.         break;  /* pktsiz = 64 (1) */
  481.     case INITA:
  482.         header[4] += MAXWINDOW;
  483.         break;
  484.     case DATA:
  485.         header[4] = 0x80 + (xxx << 3) + yyy;
  486.         /* havn't set it up for VERY LONG packets with a few
  487.            bytes yet (-128) */
  488.         if (len < PKTSIZE) {    /* short packet? */
  489.             header[4] |= 0x40;
  490.             memmove(data + 1, data, PKTSIZE - 1);
  491.             data[0] = PKTSIZE - len;
  492.         }
  493.         break;
  494.     }
  495.     if (type != DATA) {
  496.         header[1] = 9;  /* control packet size = 0 (9) */
  497.         check = (0xaaaa - header[4]) & 0xffff;
  498.     } else {
  499.         header[1] = PKTSIZ2;    /* data packet size = 64 (2) */
  500.         check = checksum(data, PKTSIZE);
  501.         i = header[4];  /* got to do this on PC for ex-or high bits */
  502.         i &= 0xff;
  503.         check = (check ^ i) & 0xffff;
  504.         check = (0xaaaa - check) & 0xffff;
  505.     }
  506.     header[2] = check & 0xff;
  507.     header[3] = (check >> 8) & 0xff;
  508.     header[5] = (header[1] ^ header[2] ^ header[3] ^ header[4]) & 0xff;
  509.  
  510.     /***** More Link Testing Mods *****/
  511. /*  switch(dpkerr[0]) {
  512.     case 'e':
  513.         data[10] = - data[10];
  514.         break;
  515.     case 'h':
  516.         header[5] = - header[5];
  517.         break;
  518.     case 'l':
  519.         return;
  520.     case 'p':
  521.         swrite(header, HDRSIZE);
  522.         if (header[1] != 9)
  523.             swrite(data, PKTSIZE - 3);
  524.         return;
  525.     default:
  526.         break;
  527.     } /**/
  528.     /***** End Link Testing Mods *****/
  529.  
  530.     swrite(header, HDRSIZE);        /* header is 6-bytes long */
  531.     if (header[1] != 9)
  532.         swrite(data, PKTSIZE);      /* data is always 64 bytes long */
  533.  
  534. } /*gspack*/
  535.  
  536.  
  537. /*
  538.     g r p a c k
  539.  
  540.     Read packet
  541.  
  542.     on return: yyy=pkrec xxx=pksent len=length<=PKTSIZE  data=*data
  543.  
  544.     ret(type)   ok
  545.     ret(EMPTY)  input buf empty
  546.     ret(ERROR)  bad header
  547.  
  548.     ret(EMPTY)  lost packet timeout
  549.     ret(ERROR)  checksum error
  550.     ret(-5)     packet size != 64
  551.  
  552.     NOTE (specifications for sread()):
  553.  
  554.     sread(buf, n, timeout)
  555.         while(TRUE) {
  556.             if (# of chars available >= n) (without dec internal counter)
  557.                 read n chars into buf (decrement internal char counter)
  558.                 break
  559.             else
  560.                 if (time > timeout)
  561.                     break
  562.         }
  563.         return(# of chars available)
  564.  
  565. */
  566.  
  567. static int grpack(yyy, xxx, len, data)
  568. int *yyy, *xxx, *len;
  569. char data[];
  570. {
  571.     unsigned int type, check, checkchk, i;
  572.     unsigned char c, c2;
  573.  
  574.     if (GOT_SYNC)
  575.         goto get_hdr;
  576.  
  577.     if (GOT_HDR)
  578.         goto get_data;
  579.  
  580.     do {
  581.         if (sread(&c, 1, timeout) == 0)
  582.             return(EMPTY);
  583.     } while ((c & 0x7f) != '\020');
  584.  
  585.     GOT_SYNC = TRUE;
  586. get_hdr:
  587.     if (sread(&grpkt[1], HDRSIZE - 1, timeout) < (HDRSIZE - 1))
  588.         return(EMPTY);
  589.     GOT_SYNC = FALSE;
  590.     /* i = grpkt[1] ^ grpkt[2] ^ grpkt[3] ^ grpkt[4] ^ grpkt[5]; */
  591.     i = (unsigned)grpkt[1] ^ (unsigned)grpkt[2] ^
  592.         (unsigned)grpkt[3] ^ (unsigned)grpkt[4] ^
  593.         (unsigned)grpkt[5];
  594.  
  595.     i &= 0xff;
  596.     printmsg(10, "prpkt %02x %02x %02x %02x %02x .. %02x ",
  597.         grpkt[1], grpkt[2], grpkt[3], grpkt[4], grpkt[5], i);
  598.  
  599.     if (i != 0) {   /* bad header */
  600.         printmsg(0, "*** bad header ***");
  601.         return(ERROR); /* I'm not sure whether "g" considers it an empty or error */
  602.     }
  603.  
  604.     GOT_HDR = TRUE;
  605.     if (grpkt[1] == 9) {    /* control packet */
  606.         *data = '\0';
  607.         *len = 0;
  608.         c = grpkt[4];
  609.         type = c >> 3;
  610.         *yyy = c & 0x07;
  611.         *xxx = 0;
  612.         check = 0;
  613.         checkchk = 0;
  614.         GOT_HDR = FALSE;
  615.     } else {    /* data packet */
  616.         if (grpkt[1] != PKTSIZ2)
  617.             return(-5);     /* can't handle packet size other than 64 */
  618. get_data:
  619.         if (sread(data, PKTSIZE, timeout) < PKTSIZE)
  620.             return(EMPTY);
  621.         GOT_HDR = FALSE;
  622.         type = 0;
  623.         c2 = grpkt[4];
  624.         c = c2 & 0x3f;
  625.         *xxx = c >> 3;
  626.         *yyy = c & 0x07;
  627.         i = grpkt[3];
  628.         i = (i << 8) & 0xff00;
  629.         check = grpkt[2];
  630.         check = i | (check & 0xff);
  631.         checkchk = checksum(data, PKTSIZE);
  632.         i = grpkt[4] | 0x80;
  633.         i &= 0xff;
  634.         checkchk = 0xaaaa - (checkchk ^ i);
  635.         checkchk &= 0xffff;
  636.         if (checkchk != check) {
  637.             printmsg(4, "*** checksum error ***");
  638.             return(ERROR);
  639.         }
  640.         *len = PKTSIZE;
  641.         /* Haven't set it up for very long pkts yet (>128).  RH Lamb */
  642.         if (c2 & 0x40) {
  643.             int ii;
  644.             ii = (data[0] & 0xff);
  645.             *len = (*len - ii) & 0xff;
  646.             memmove(data, data + 1, *len);
  647.         }
  648.         data[*len] = '\0';
  649.     }
  650.  
  651.     printmsg(12, "receive packet type %d, yyy=%d, xxx=%d, len=%d",
  652.         type, *yyy, *xxx, *len);
  653.     printmsg(13, " checksum rec=%04x comp=%04x\ndata=|%s|",
  654.         check, checkchk, data);
  655.  
  656.     return(type);
  657.  
  658. } /*grpack*/
  659.  
  660.  
  661. /*
  662.     c h e c k s u m
  663. */
  664.  
  665. unsigned checksum(data, len)
  666. int len;
  667. char data[];
  668. {
  669.     unsigned int i, j, tmp, chk1, chk2;
  670.     chk1 = 0xffff;
  671.     chk2 = 0;
  672.     j = len;
  673.     for (i = 0; i < len; i++) {
  674.         if (chk1 & 0x8000) {
  675.             chk1 <<= 1;
  676.             chk1++;
  677.         } else {
  678.             chk1 <<= 1;
  679.         }
  680.         tmp = chk1;
  681.         chk1 += (data[i] & 0xff);
  682.         chk2 += chk1 ^ j;
  683.         if ((chk1 & 0xffff) <= (tmp & 0xffff))
  684.             chk1 ^= chk2;
  685.         j--;
  686.     }
  687.     return(chk1 & 0xffff);
  688.  
  689. } /*checksum*/
  690.  
  691.  
  692. #if FALSE
  693.  
  694. /*
  695.     gwrmsg - send a null terminated string out
  696. */
  697.  
  698. gwrmsg(typ, buf)
  699. char    typ;
  700. char    *buf;   /* null terminated */
  701. {
  702.  
  703. } /*gwrmsg*/
  704.  
  705.  
  706. /*
  707.     grdmsg - read a null terminated string
  708. */
  709.  
  710. grdmsg(buf)
  711. char    *buf;
  712. {
  713.  
  714. } /*grdmsg*/
  715.  
  716.  
  717. /*
  718.     gwrdata - read a file and send it out
  719. */
  720.  
  721. gwrdata(f)
  722. {
  723.  
  724. } /*gwrdata*/
  725.  
  726.  
  727. /*
  728.     grrdata - read in data and send to file
  729. */
  730.  
  731. grrdata(f)
  732. {
  733.  
  734. } /*grrdata*/
  735.  
  736.  
  737. /*
  738.     grdblk - read a block of data in
  739. */
  740.  
  741. grdblk(blk, len)
  742. {
  743.  
  744. } /*grdblk*/
  745.  
  746.  
  747. /*
  748.     gwrblk - write out a block of data
  749. */
  750.  
  751. gwrblk(blk, len)
  752. {
  753.  
  754. } /*gwrblk*/
  755.  
  756. #endif
  757.